require 'logger'
require 'depac-common/constants'
#def log
#  logger=Logger.new(STDOUT)
#  logger.level=Logger::DEBUG
#  logger
#end

module MongoShards
  module CMD
    def self.included service
      service.class_eval do
        def isConnectable
          result=performdbcmd %{print("OK")}
          result.include? "OK"
        end
        
        def performdbcmd cmd,defaultdb="test"
          _cmd=mongocmd cmd,self.insIP,self.insPort,defaultdb
          result=_exec _cmd
        end
        
        
        def performdbcmdlocal cmd,defaultdb="test"
           
           _cmd=mongocmd cmd,self.insIP,self.insPort,defaultdb
           result=`#{_cmd}`
           Depac::Log.write("INFO", "mongoshard.performdbcmdlocal.result: #{result}")
           return result
         end
         
        
        def wait4Connected
          while !isConnectable
            yield(self) if block_given?
            Depac::Log.write("INFO", "wait connection")
          end
        end
        def isLocal?
          host=="127.0.0.1" || host=="localhost" || port.nil?
        end
        
        def insIP
          @insIP||= (isLocal?)? performhostcmd("d.ip #{insname}") : host
          @insIP.gsub!("\n","")
          Depac::Log.write("INFO", "#{@insIP}")
          @insIP
        end
        
        def insPort
          @insPort||= (isLocal?)? 27017 : port
          @insPort
        end

        def performhostcmd cmd
          if isLocal?
            Depac::Log.write("INFO", "run command on server #{@insIP} #{cmd}")
            p `bash -lc '#{cmd}'`
          else
            _exec cmd
          end
        end
        
        def mongocmd cmd,_host,_port,defaultdb="test"
          %{cat<<MCMD|mongo #{_host}:#{_port}/#{defaultdb}
               #{cmd}
MCMD}
        end
        
        
        def mongocmdnew cmd,_host,_port,defaultdb="test"
            
            if isLocal?
              %{cat<<MCMD|mongo #{_host}:#{_port}/#{defaultdb}
                            #{cmd}
              MCMD}
            else           
               cmd="mongo "+cmd
               Depac::Log.write("INFO", "molngoshard.mongocmdnew CMD: #{cmd}")
              _exec cmd
              
            end
       
        end
        
        def _exec cmd
          #remotecmd="ssh -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{self.host} #{cmd}"
          remotecmd=  "ssh -t -t -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} root@#{self.host} -o StrictHostKeyChecking=no '#{cmd}'"
          Depac::Log.write("INFO", "#{remotecmd}")
          result=`#{remotecmd}`
          Depac::Log.write("INFO", "#{result}")
          result
        end
      end
    end
  end
  
  class Service
    attr_accessor :host,:port,:insname
    def initialize _insname,targethostport
      @host,@port=targethostport.split(":")
      @insname=_insname
    end
  end
  
  class MongoS<Service
    include CMD
    attr_accessor :mongoconfs,:shards,:replicsets,:logpath,:dimagename
    def initialize insname,hostport,logpath="/data/mongos"
      super insname,hostport
      @datapath="#{logpath}/#{@insname}/"
      @logpath="#{@datapath}/mongoslog"
      @dimagename="mongos:cbt"
      yield self if block_given?
    end
    def addConf *confs
      @mongoconfs||=[]
      @mongoconfs=@mongoconfs+confs if not confs.empty?
    end
    
    def addShards *_shards
      @shards||=[]
      @shards=@shards+_shards
    end

    def addReplicSet *_replicsets
      @replicsets||=[]
      @replicsets=@replicsets+_replicsets
      Depac::Log.write("INFO", "Adding replicSet")
      wait4Connected{
          Depac::Log.write("INFO", "#{insIP}:#{insPort} is not connectable now, keep trying in 5 sec")
          sleep 5;
          Depac::Log.write("INFO", "try agian")
      }

      _replicsets.each{|_reps|
        result=self.performdbcmd %{db.runCommand({addshard:"#{_reps.to_s}",name:"#{insname}", maxsize:20480,allowLocal:true});},"admin"
        raise "add replset failed" if result.include? "Error"
      }
    end
    
    def create
      _exposedport=_logpath=""
      _exposedport="-p #{port}:27017" if port
      _logpath="-v #{logpath}:/data/log" if logpath
      configdbs=@mongoconfs.map{|_conf|
        "#{_conf.insIP}:#{_conf.insPort }"
      }.join(",")
      _env=%{-e "configdbs=#{configdbs}"} 
      createpath="mkdir -p #{@datapath};touch #{logpath};" if logpath
      cmd=%{#{createpath}docker rm -f "#{insname}";docker run -d --name "#{insname}" #{_exposedport} #{_env} #{_logpath}  #{dimagename} }


      result=performhostcmd cmd
      raise "failed to create docker container" if result.empty?
      self
    end
  end
  
  class MongoConf<Service
    include CMD
    attr_accessor :datapath,:logpath,:dimagename
    def initialize insname,hostport,datapath="/data/confdb"
      super insname,hostport
      @datapath="#{datapath}/#{@insname}/db"
      @logpath="#{@datapath}/../conflog"
      @dimagename="mongoconf:cbt"
    end


    
    def create
      _exposedport=_datapath=_logpath=""
      _exposedport="-p #{port}:27017" if port
      _datapath="-v #{datapath}:/data/db" if datapath
      _logpath="-v #{logpath}:/data/log" if logpath
      createpath="mkdir -p #{datapath};touch #{logpath};" if datapath
      cmd=%{#{createpath}docker rm -f "#{insname}";docker run -d --name "#{insname}" #{_exposedport} #{_datapath} #{_logpath}  #{dimagename} }
      result=performhostcmd cmd
      raise "failed to create docker container" if result.empty?
      self

    end

  end
  
  class ReplicSet
    include CMD
    attr_accessor :shards,:name,:host
    def initialize replicname,&block
      @name=replicname
      @shards=block.call(self) if block
    end

    def conf
      Depac::Log.write("INFO", "MongoShards.ReplicSet.conf.shards: #{shards}")
      members=shards.inject([]){|r,shard|
        r<<%{{_id:#{r.count},host:'#{shard.insIP}:#{shard.insPort}'}}
        r
      }
        %{
                    {_id:'#{name}',members:[#{members.join(",")}]}
                    
         }
    end
    
    def cleanlocaldb
      shards.each{|_shard|
        #clean local db
        self.host=_shard.host
        _shard.wait4Connected{
          Depac::Log.write("INFO", "#{_shard.insIP}:#{_shard.insPort} is not connectable now, keep trying in 10 sec")
          sleep 10;
          Depac::Log.write("INFO", "try agian")
          }
        _shard.performdbcmd "db.dropDatabase()","local"
      }
    end
    
    def to_s
      _result=shards.map{|_shard| "#{_shard.insIP}:#{_shard.insPort}"}
      Depac::Log.write("INFO", "MongoShards.ReplicSet.to_s: ------------------") 
      Depac::Log.write("INFO", "MongoShards.ReplicSet.to_s original _result : #{_result}") 
      Depac::Log.write("INFO", "MongoShards.ReplicSet.to_s: ------------------") 
      _result=%{#{name}/#{_result.join(",")}}
      Depac::Log.write("INFO", "MongoShards.ReplicSet.to_s: ------------------") 
      Depac::Log.write("INFO", "MongoShards.ReplicSet.to_s finial _result : #{_result}") 
      _result
    end
    
    def create
      "" if shards.empty?
      shard=shards.first
      #result=shard.performdbcmd "config=#{conf};rs.initiate(config);"
      result=shard.performdbcmdlocal "config=#{conf};rs.initiate(config);"
      resultx=result.include? %{"ok" : 1}
      raise "can't config replicSet as #{conf},result is #{result}" if !resultx
      self
    end
    
  end
  
  class MongoShard<Service
    include CMD
    attr_accessor :replicname,:datapath,:logpath,:dimagename
    def initialize insname,hostport,replicname,datapath="/data/shardingdb"
      super insname,hostport
      @replicname=replicname
      @datapath="#{datapath}/#{@insname}/db"
      @logpath="#{@datapath}/../shardlog"
      @dimagename="mongoshard:cbt"
    end
    def create
      _exposedport=_datapath=_logpath=""
      _exposedport="-p #{port}:27017" if port
      _datapath="-v #{datapath}:/data/db" if datapath
      _logpath="-v #{logpath}:/data/log" if logpath
      _env=%{-e "opts=--replSet #{replicname}"} if replicname
      createpath="mkdir -p #{datapath};touch #{logpath};" if datapath
      cmd=%{#{createpath}docker rm -f "#{insname}";docker run -d --name "#{insname}" #{_exposedport} #{_env} #{_datapath} #{_logpath}  #{dimagename} }
      result=performhostcmd cmd
      raise "failed to create docker container" if result.empty?
      self

    end
    
  end

end

#include MongoShards
=begin
host="10.80.181.181"
hostport="#{host}:27018"
MongoS.new(:mongos1,hostport){|mongos|
    replicset=ReplicSet.new(:RSX1){|rset|
      shard1=MongoShard.new "#{rset.name}1","#{host}:27020",rset.name
      shard2=MongoShard.new "#{rset.name}2","#{host}:27021",rset.name
  #    shard1.create
  #    shard2.create
      [shard1,shard2]
    }
  #  replicset.cleanlocaldb
  #  replicset.create
  mongconf=MongoConf.new(:mongoconf1,"#{host}")
  mongconf.create
  mongos.addConf(mongconf)
  mongos.create
  mongos.addReplicSet replicset
}
=end
